package cn.com.lms.business.livecourse.util;

import cn.com.lms.business.livecourse.constant.AliRtcConstants;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SignatureException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * AliSignUtil
 *
 * @author barry
 * @date 2020/3/24
 **/
public class AliSignUtil {

    /**
     * 获取已api签名的请求url
     * @param serviceDomain   服务域名 如 https://rtc.aliyuncs.com
     * @param httpMethod    http请求方法 如 GET
     * @param privateParams 对应服务的私有参数
     * @return
     */
    public static String signRequestUrl(String serviceDomain, String httpMethod, Map<String, String> privateParams) {
        // 公共参数
        Map<String, String> publicParams = generatePublicParams();
        List<String> allParams = getAllParams(publicParams, privateParams);
        String cqs = getCQS(allParams);
        /*构造待签名的字符串*/
        String stringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(cqs);
//        System.out.println("stringToSign = " + stringToSign);
        byte[] bytes = hmacSHA1Signature(AliRtcConstants.ACCESS_KEY_SECRET, stringToSign);
        String sign = "";
        try {
            sign = newStringByBase64(bytes);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        String requestUrl = serviceDomain + "/?";
        for (String param : allParams) {
            requestUrl += "&" + param;
        }
        requestUrl += "&Signature=" + percentEncode(sign);
//        System.out.println("requestUrl = " + requestUrl);
        return requestUrl;
    }

    /*生成公共参数 （没有Signature）*/
    private static Map<String, String> generatePublicParams() {
        String timestamp = generateTimestamp();
        String signatureNonce = generateRandom();
        // 公共参数
        Map<String, String> publicParams = new HashMap<String, String>();
        publicParams.put("Format", "JSON");
        publicParams.put("Version", "2018-01-11");
        publicParams.put("AccessKeyId", AliRtcConstants.ACCESS_KEY_ID);
        publicParams.put("SignatureMethod", "HMAC-SHA1");
        publicParams.put("Timestamp", timestamp);
        publicParams.put("SignatureVersion", "1.0");
        publicParams.put("SignatureNonce", signatureNonce);
        return publicParams;
    }

    /*对所有参数名称和参数值做URL编码*/
    private static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) {
        List<String> encodeParams = new ArrayList<String>();
        if (publicParams != null) {
            for (String key : publicParams.keySet()) {
                String value = publicParams.get(key);
                //将参数和值都urlEncode一下。
                String encodeKey = percentEncode(key);
                String encodeVal = percentEncode(value);
                encodeParams.add(encodeKey + "=" + encodeVal);
            }
        }
        if (privateParams != null) {
            for (String key : privateParams.keySet()) {
                String value = privateParams.get(key);
                //将参数和值都urlEncode一下。
                String encodeKey = percentEncode(key);
                String encodeVal = percentEncode(value);
                encodeParams.add(encodeKey + "=" + encodeVal);
            }
        }
        return encodeParams;
    }

    /*获取 CanonicalizedQueryString*/
    private static String getCQS(List<String> allParams) {
        ParamsComparator paramsComparator = new ParamsComparator();
        Collections.sort(allParams, paramsComparator);
        String cqString = "";
        for (int i = 0; i < allParams.size(); i++) {
            cqString += allParams.get(i);
            if (i != allParams.size() - 1) {
                cqString += "&";
            }
        }
        return cqString;
    }
    /*字符串参数比较器，按字母序升序*/
    private static class ParamsComparator implements Comparator<String> {
        @Override
        public int compare(String lhs, String rhs) {
            return lhs.compareTo(rhs);
        }
    }

    /*构造待签名的字符串*/
//    String StringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString);
    /*特殊字符替换为转义字符*/
    private static String percentEncode(String value) {
        try {
            String urlEncodeOrignStr = URLEncoder.encode(value, "UTF-8");
            String plusReplaced = urlEncodeOrignStr.replace("+", "%20");
            String starReplaced = plusReplaced.replace("*", "%2A");
            String waveReplaced = starReplaced.replace("%7E", "~");
            return waveReplaced;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return value;
    }

    /*计算待签名字符串的HMAC值*/
    private static byte[] hmacSHA1Signature(String accessKeySecret, String stringToSign) {
        try {
            String key = accessKeySecret + "&";
            try {
                SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
                Mac mac = Mac.getInstance("HmacSHA1");
                mac.init(signKey);
                return mac.doFinal(stringToSign.getBytes());
            } catch (Exception e) {
                throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
            }
        } catch (SignatureException e) {
            e.printStackTrace();
        }
        return null;
    }
    /*编码得到最终签名值
按照 Base64 编码规则将1.3中计算得到的HMAC值编码成字符串，得到最终签名值（Signature）。*/
    private static String newStringByBase64(byte[] bytes)
            throws UnsupportedEncodingException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return new String(new BASE64Encoder().encode(bytes));
    }

    /*生成当前UTC时间戳Time*/
    private static String generateTimestamp() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df.format(date);
    }
    /*生成随机数SignatureNonce*/
    private static String generateRandom() {
        String signatureNonce = UUID.randomUUID().toString();
        return signatureNonce;
    }
}
